/* Configuration macros.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KP_CONFIG__
#define __KP_CONFIG__   1

// Platform detection.

#if defined (sun) || defined (__sun)
#  define KP_PLATFORM_SOLARIS
#  define KP_KNOWN_PLATFORM
#elif defined (_hpux) || defined (__hpux)
#  define KP_PLATFORM_HPUX
#  define KP_KNOWN_PLATFORM
#elif defined (__GNU__) || defined (__gnu_hurd__)
#  define KP_PLATFORM_GNU
#  define KP_KNOWN_PLATFORM
#elif defined (linux) || defined (__linux) || defined (__linux__)
#  define KP_PLATFORM_LINUX
#  define KP_KNOWN_PLATFORM
#elif defined (__OS2__) || defined (__EMX__)
#  define KP_PLATFORM_OS2
#  define KP_KNOWN_PLATFORM
#elif defined (__NetBSD__)
#  define KP_PLATFORM_NETBSD
#  define KP_KNOWN_PLATFORM
#elif defined (__OpenBSD__)
#  define KP_PLATFORM_OPENBSD
#  define KP_KNOWN_PLATFORM
#elif defined (__FreeBSD__) || defined (__DragonFly__)
#  define KP_PLATFORM_FREEBSD
#  define KP_KNOWN_PLATFORM
#elif defined (macintosh) || defined (__APPLE__)
#  define KP_PLATFORM_MAC
#  define KP_KNOWN_PLATFORM
#elif defined (__BEOS__) || defined (__HAIKU__)
#  define KP_PLATFORM_BEOS
#  define KP_KNOWN_PLATFORM
#elif defined (__CYGWIN__)
#  define KP_PLATFORM_WINDOWS
#  define KP_KNOWN_PLATFORM
#elif defined (__MINGW32__)
#  define KP_PLATFORM_WINDOWS
#  define KP_KNOWN_PLATFORM
#elif defined (_WIN32) || defined (__WIN32__) ||   \
    defined (_WIN64) || defined (_WIN32_WCE)
#  define KP_PLATFORM_WINDOWS
#  define KP_KNOWN_PLATFORM
#endif

#ifndef KP_KNOWN_PLATFORM
#  error "Unsupported platform!"
#endif

// Architecture detection.
#if defined (__alpha__) || defined (_M_ALPHA) || defined (__alpha)
#  define KP_ARCH_ALPHA
#  define KP_KNOWN_ARCH
#elif defined (__amd64) || defined (__amd64__) ||   \
    defined (__x86_64__) || defined (__x86_64) ||   \
    defined (_M_X64) || defined (_M_AMD64)
#  define KP_ARCH_X8664
#  define KP_KNOWN_ARCH
#elif defined (__aarch64__)
#  define KP_ARCH_ARM64
#  define KP_KNOWN_ARCH
#elif defined (__arm__) || defined (__thumb__) ||   \
    defined (_M_ARM) || defined (_M_ARMT)
#  define KP_ARCH_ARM32
#  define KP_KNOWN_ARCH
#elif defined (__i386) || defined (__i386__) || defined (_M_IX86) ||   \
    defined (__X86__) || defined (_X86_) || defined (__INTEL__)
#  define KP_ARCH_I386
#  define KP_KNOWN_ARCH
#elif defined (__ia64__) || defined (__ia64) ||   \
    defined (_M_IA64) || defined (__itanium__)
#  define KP_ARCH_IA64
#  define KP_KNOWN_ARCH
#elif defined (__mips) || defined (__mips__) || defined (__MIPS__)
#  define KP_ARCH_MIPS
#  define KP_KNOWN_ARCH
#elif defined (__powerpc64__) || defined (__ppc64__)
#  define KP_ARCH_PPC64
#  define KP_KNOWN_ARCH
#elif defined (__powerpc) || defined (__ppc__) || defined (_M_PPC)
#  define KP_ARCH_PPC32
#  define KP_KNOWN_ARCH
#elif defined (__sparc) || defined (__sparc__)
#  define KP_ARCH_SPARC
#  define KP_KNOWN_ARCH
#endif

#ifndef KP_KNOWN_ARCH
#  error "Unsupported architecture!"
#endif

#ifdef __clang__
#  if defined (__has_include) && __has_include (<endian.h>)
#    define KP_HAS_ENDIAN_H
#  endif
#endif

// Endianness detection.
# if defined (__BYTE_ORDER__)
#   if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#     define KP_LITTLE_ENDIAN   1
#   else
#     define KP_LITTLE_ENDIAN   0
#   endif
#elif defined (__GLIBC__) || defined (KP_HAS_ENDIAN_H)
  #include <endian.h>
#  if __BYTE_ORDER == __LITTLE_ENDIAN
#    define KP_LITTLE_ENDIAN   1
#  elif __BYTE_ORDER == __BIG_ENDIAN
#    define KP_LITTLE_ENDIAN   0
#  else
#    error "Unsupported endianness!"
#  endif
#else
#  if defined (__BIG_ENDIAN__) || defined (__ARMEB__) ||   \
      defined (__THUMBEB__) || defined (__AARCH64EB__) ||   \
      defined (_MIPSEB) || defined (__MIPSEB) || defined (__MIPSEB__)
#    define KP_LITTLE_ENDIAN   0
#  elif defined (__LITTLE_ENDIAN__) || defined (__ARMEL__) ||   \
      defined (__THUMBEL__) || defined (__AARCH64EL__) ||   \
      defined (_MIPSEL) || defined (__MIPSEL) || defined (__MIPSEL__)
#    define KP_LITTLE_ENDIAN   1
#  endif
#endif

#undef KP_HAS_ENDIAN_H

// Determine endianness 'empirically'.
#ifndef KP_LITTLE_ENDIAN
#  if defined (KP_ARCH_MIPS) || defined (KP_ARCH_SPARC)
#    define KP_LITTLE_ENDIAN   0
#  elif defined (KP_ARCH_I386) || defined (KP_ARCH_X8664) ||   \
      defined (KP_ARCH_ALPHA) || defined (KP_ARCH_ARM32) ||   \
      defined (KP_ARCH_ARM64)
#    define KP_LITTLE_ENDIAN   1
#  endif
#endif

#ifndef KP_LITTLE_ENDIAN
#  error "Cannot determine endianness!"
#endif

#if defined (__unix) || defined (__unix__) || defined (unix)
#  define KP_PLATFORM_UNIX
#endif

#if defined (KP_PLATFORM_FREEBSD) ||   \
    defined (KP_PLATFORM_OPENBSD) ||   \
    defined (KP_PLATFORM_NETBSD)
#  define KP_PLATFORM_BSD
#endif

// Finally, determine if we're on a 64-bit platform.
#if defined (KP_ARCH_IA64) || defined (KP_ARCH_X8664) ||   \
    defined (KP_ARCH_ALPHA) || (defined (KP_ARCH_MIPS) &&   \
      (_MIPS_SZLONG - 0) == 64) ||   \
    defined (KP_ARCH_PPC64) || defined (KP_ARCH_ARM64) ||   \
    (defined (KP_ARCH_SPARC) && defined (__arch64__))
#  define KP_ARCH_WIDE
#endif

// Adjust for x32.
#if defined (KP_ARCH_X8664) && defined (_ILP32) || defined (__ILP32__)
#  undef KP_ARCH_X8664
#  define KP_ARCH_X32
#  undef KP_ARCH_WIDE
#endif

#define KP_EXPORT   extern

#endif
